<!doctype html><html lang=cn-zh><head><meta charset=UTF-8><meta name=viewport content="width=device-width,initial-scale=1"><meta http-equiv=X-UA-Compatible content="IE=edge"><style type=text/css>body{font-family:monospace}</style><title>获取存储在又拍云CDN中视频的时长</title>
<meta name=description content="A blog maintained by Vimiix."><link rel=stylesheet href=/css/style.css><script>var _hmt=_hmt||[];(function(){var e,t=document.createElement("script");t.src="https://hm.baidu.com/hm.js?7c24231917964240bae97e813810620c",e=document.getElementsByTagName("script")[0],e.parentNode.insertBefore(t,e)})()</script></head><body><header>====================<br>== Hi, I'm Vimiix ==<br>====================<div style=float:right;color:gray;font-size:x-large>Get hands dirty.</div><br><p><nav><a href=https://www.vimiix.com/><b>首页</b></a>.
<a href=/posts/><b>文章列表</b></a>.
<a href=/projects/><b>开源项目</b></a>.
<a href=/tags/><b>标签</b></a>.
<a href=/friends/><b>友链</b></a>.
<a href=/about/><b>关于我</b></a>.
<a href=/index.xml><b>RSS</b></a>.</nav></p></header><main><article><h1>获取存储在又拍云CDN中视频的时长</h1><b><time>2019.01.30 16:22</time></b>
<a href=/tags/note>note</a>
<a href=/tags/video>video</a>
<a href=/tags/cdn>CDN</a>
<a href=/tags/%E5%8F%88%E6%8B%8D%E4%BA%91>又拍云</a><div><h3 id=前置条件>前置条件</h3><ul><li><p>可用的账户</p></li><li><p>安装又拍云 Python 版本的 SDK：</p></li></ul><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>pip install upyun
</span></span></code></pre></div><p>（官方代码开源地址：<a href=https://github.com/upyun/python-sdk/>https://github.com/upyun/python-sdk/</a> )</p><h3 id=继承官方类编写需求代码>继承官方类，编写需求代码</h3><p>官方的 SDK 中只提供了一些基础的方法，可以在源码的依赖文件中看到，只依赖 <code>requests</code> 一个包，所以，官方库其实是实现了对网络交互的封装，没有实际的业务功能，真正又拍云 CDN 提供的服务，需要自己通过这个 SDK 发起 api 请求来获取。</p><p>既然这样，那就继承官方提供的 SDK，增加自己需要的方法：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-python data-lang=python><span style=display:flex><span><span style=color:#f92672>from</span> upyun <span style=color:#f92672>import</span> UpYun
</span></span><span style=display:flex><span><span style=color:#f92672>from</span> config <span style=color:#f92672>import</span> BUCKET, USER, PASSWORD
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#66d9ef>class</span> <span style=color:#a6e22e>MyUpYun</span>(UpYun):
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>pass</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>worker <span style=color:#f92672>=</span> MyUpYun(BUCKET, USER, PASSWORD)
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>__all__ <span style=color:#f92672>=</span> [<span style=color:#e6db74>&#39;worker&#39;</span>]
</span></span></code></pre></div><p>OK，这样就实现了基本的继承，并对外提供一个 <code>worker</code> 实例，这个实例就是一个已经是登录状态的”打工仔“了。在其他模块调用 <code>worker</code> ，就可以使用又拍云 SDK 提供的工具了。</p><p>接下来就需要在我自己写的这个 <code>MyUpYun</code> 类中实现特定的功能方法了。</p><blockquote><p>又拍云提供的全部云端服务可用移步这里查看：<a href=http://docs.upyun.com/>http://docs.upyun.com/</a></p></blockquote><p>获取视频的元信息，属于 <strong><a href=http://docs.upyun.com/cloud/>云处理</a></strong> 服务。</p><p>由于每次请求远程的 api 需要在 <code>headers</code> 中传递固定的签名认证，所以就需要先写一个生成签名的方法：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-python data-lang=python><span style=display:flex><span><span style=color:#f92672>from</span> upyun <span style=color:#f92672>import</span> UpYun
</span></span><span style=display:flex><span><span style=color:#f92672>from</span> config <span style=color:#f92672>import</span> BUCKET, USER, PASSWORD
</span></span><span style=display:flex><span><span style=color:#f92672>from</span> upyun.modules.httpipe <span style=color:#f92672>import</span> cur_dt
</span></span><span style=display:flex><span><span style=color:#f92672>from</span> upyun.modules.sign <span style=color:#f92672>import</span> make_signature
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#66d9ef>class</span> <span style=color:#a6e22e>MyUpYun</span>(UpYun):
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>def</span> <span style=color:#a6e22e>gen_signature</span>(self, method, uri):
</span></span><span style=display:flex><span>        <span style=color:#e6db74>&#34;&#34;&#34;
</span></span></span><span style=display:flex><span><span style=color:#e6db74>        args:
</span></span></span><span style=display:flex><span><span style=color:#e6db74>        	method: 请求的 HTTP 方法
</span></span></span><span style=display:flex><span><span style=color:#e6db74>        	uri: api 地址
</span></span></span><span style=display:flex><span><span style=color:#e6db74>        &#34;&#34;&#34;</span>
</span></span><span style=display:flex><span>        dt <span style=color:#f92672>=</span> cur_dt()
</span></span><span style=display:flex><span>        <span style=color:#66d9ef>return</span> make_signature(username<span style=color:#f92672>=</span>self<span style=color:#f92672>.</span>username,
</span></span><span style=display:flex><span>                              password<span style=color:#f92672>=</span>self<span style=color:#f92672>.</span>password,
</span></span><span style=display:flex><span>                              auth_server<span style=color:#f92672>=</span>self<span style=color:#f92672>.</span>auth_server,
</span></span><span style=display:flex><span>                              method<span style=color:#f92672>=</span>method,
</span></span><span style=display:flex><span>                              uri<span style=color:#f92672>=</span>uri,
</span></span><span style=display:flex><span>                              date<span style=color:#f92672>=</span>dt)
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>    <span style=color:#f92672>...</span>
</span></span></code></pre></div><p>这里实现了一个 <code>gen_signature</code> 方法，它会帮助我生成在请求 api 接口时所需要的加密后的签名字符串。可以看到 ，在函数内部，传递一系列 <code>self.xxx</code> 的参数，我的本地类中并没有定义这些 property，但不用担心，因为我们是继承的官方库，在实例化的时候，已经把用户认证信息穿进去了，对应的这个 property 都已经在父类里定义好了。后面类似直接用到的属性，也是这样。</p><p>到这里，签名也准备好了，接下来就是请求获取视频元信息的 api。</p><blockquote><p>获取音视频元信息文档：<a href=http://docs.upyun.com/cloud/sync_video/#_14>http://docs.upyun.com/cloud/sync_video/#_14</a></p></blockquote><p>从文档可以看到，这里需要通过 <code>POST</code> 方法来请求 <code>http://p1.api.upyun.com/&lt;service>/avmeta/get_meta</code></p><p>接口。但是需要在 body 中传递一个 <code>source</code> 参数，这个 <code>source</code> 其实就是对应的文件在又拍云的 bucket 中存储的文件路径，如果你没有对你的 bucket 做目录分级，可以直接传文件名。</p><p>因为 upyun SDK 内部的 HTTP 请求都是使用的 requests 库，所以，其实请求结果拿到的 response 其实是 requests 中的 <code>Response</code> 对象，可以很方便的通过 <code>.json()</code> 来获取字典结构的数据。</p><p>下面是获取视频元数据的方法代码：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-python data-lang=python><span style=display:flex><span><span style=color:#66d9ef>class</span> <span style=color:#a6e22e>MyUpYun</span>(UpYun):
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>    <span style=color:#66d9ef>def</span> <span style=color:#a6e22e>get_meta</span>(self, source):
</span></span><span style=display:flex><span>        method, dt, headers <span style=color:#f92672>=</span> <span style=color:#e6db74>&#39;POST&#39;</span>, cur_dt(), {}
</span></span><span style=display:flex><span>        host <span style=color:#f92672>=</span> <span style=color:#e6db74>&#39;p1.api.upyun.com&#39;</span>
</span></span><span style=display:flex><span>        uri <span style=color:#f92672>=</span> <span style=color:#e6db74>&#39;/</span><span style=color:#e6db74>%s</span><span style=color:#e6db74>/</span><span style=color:#e6db74>%s</span><span style=color:#e6db74>&#39;</span> <span style=color:#f92672>%</span> (self<span style=color:#f92672>.</span>service, <span style=color:#e6db74>&#39;avmeta/get_meta&#39;</span>)
</span></span><span style=display:flex><span>        data <span style=color:#f92672>=</span> json<span style=color:#f92672>.</span>dumps({<span style=color:#e6db74>&#39;source&#39;</span>: source})
</span></span><span style=display:flex><span>        headers[<span style=color:#e6db74>&#39;Authorization&#39;</span>] <span style=color:#f92672>=</span> self<span style=color:#f92672>.</span>gen_signature(method, uri)
</span></span><span style=display:flex><span>        headers[<span style=color:#e6db74>&#39;Date&#39;</span>] <span style=color:#f92672>=</span> dt
</span></span><span style=display:flex><span>        resp <span style=color:#f92672>=</span> self<span style=color:#f92672>.</span>hp<span style=color:#f92672>.</span>do_http_pipe(
</span></span><span style=display:flex><span>            method, host, uri, value<span style=color:#f92672>=</span>data, headers<span style=color:#f92672>=</span>headers, stream<span style=color:#f92672>=</span><span style=color:#66d9ef>False</span>
</span></span><span style=display:flex><span>        )
</span></span><span style=display:flex><span>        content <span style=color:#f92672>=</span> resp<span style=color:#f92672>.</span>json()
</span></span><span style=display:flex><span>        <span style=color:#66d9ef>return</span> content
</span></span></code></pre></div><p>在业务层，通过调用 <code>worker.get_meta(source)</code> 即可获取到对应视频文件的元信息了。</p><p>在返回的结果中数据示例是这样的，对应到每个字段的定义官方文档中都有介绍：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{
</span></span><span style=display:flex><span>  <span style=color:#f92672>&#34;streams&#34;</span>: [
</span></span><span style=display:flex><span>    {
</span></span><span style=display:flex><span>      <span style=color:#f92672>&#34;index&#34;</span>: <span style=color:#ae81ff>0</span>,
</span></span><span style=display:flex><span>      <span style=color:#f92672>&#34;type&#34;</span>: <span style=color:#e6db74>&#34;video&#34;</span>,
</span></span><span style=display:flex><span>      <span style=color:#f92672>&#34;video_fps&#34;</span>: <span style=color:#ae81ff>25</span>,
</span></span><span style=display:flex><span>      <span style=color:#f92672>&#34;video_height&#34;</span>: <span style=color:#ae81ff>236</span>,
</span></span><span style=display:flex><span>      <span style=color:#f92672>&#34;video_width&#34;</span>: <span style=color:#ae81ff>426</span>,
</span></span><span style=display:flex><span>      <span style=color:#f92672>&#34;codec_desc&#34;</span>: <span style=color:#e6db74>&#34;H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10&#34;</span>,
</span></span><span style=display:flex><span>      <span style=color:#f92672>&#34;codec&#34;</span>: <span style=color:#e6db74>&#34;h264&#34;</span>,
</span></span><span style=display:flex><span>      <span style=color:#f92672>&#34;bitrate&#34;</span>: <span style=color:#ae81ff>99608</span>,
</span></span><span style=display:flex><span>      <span style=color:#f92672>&#34;duration&#34;</span>: <span style=color:#ae81ff>184.8</span>,
</span></span><span style=display:flex><span>      <span style=color:#f92672>&#34;metadata&#34;</span>: {
</span></span><span style=display:flex><span>        <span style=color:#f92672>&#34;handler_name&#34;</span>: <span style=color:#e6db74>&#34;VideoHandler&#34;</span>,
</span></span><span style=display:flex><span>        <span style=color:#f92672>&#34;language&#34;</span>: <span style=color:#e6db74>&#34;und&#34;</span>
</span></span><span style=display:flex><span>      }
</span></span><span style=display:flex><span>    },
</span></span><span style=display:flex><span>    {
</span></span><span style=display:flex><span>      <span style=color:#f92672>&#34;index&#34;</span>: <span style=color:#ae81ff>1</span>,
</span></span><span style=display:flex><span>      <span style=color:#f92672>&#34;type&#34;</span>: <span style=color:#e6db74>&#34;audio&#34;</span>,
</span></span><span style=display:flex><span>      <span style=color:#f92672>&#34;audio_channels&#34;</span>: <span style=color:#ae81ff>2</span>,
</span></span><span style=display:flex><span>      <span style=color:#f92672>&#34;audio_samplerate&#34;</span>: <span style=color:#ae81ff>44100</span>,
</span></span><span style=display:flex><span>      <span style=color:#f92672>&#34;codec_desc&#34;</span>: <span style=color:#e6db74>&#34;AAC (Advanced Audio Coding)&#34;</span>,
</span></span><span style=display:flex><span>      <span style=color:#f92672>&#34;codec&#34;</span>: <span style=color:#e6db74>&#34;aac&#34;</span>,
</span></span><span style=display:flex><span>      <span style=color:#f92672>&#34;bitrate&#34;</span>: <span style=color:#ae81ff>48005</span>,
</span></span><span style=display:flex><span>      <span style=color:#f92672>&#34;duration&#34;</span>: <span style=color:#ae81ff>184.855011</span>,
</span></span><span style=display:flex><span>      <span style=color:#f92672>&#34;metadata&#34;</span>: {
</span></span><span style=display:flex><span>        <span style=color:#f92672>&#34;handler_name&#34;</span>: <span style=color:#e6db74>&#34;SoundHandler&#34;</span>,
</span></span><span style=display:flex><span>        <span style=color:#f92672>&#34;language&#34;</span>: <span style=color:#e6db74>&#34;und&#34;</span>
</span></span><span style=display:flex><span>      }
</span></span><span style=display:flex><span>    }
</span></span><span style=display:flex><span>  ],
</span></span><span style=display:flex><span>  <span style=color:#f92672>&#34;format&#34;</span>: {
</span></span><span style=display:flex><span>    <span style=color:#f92672>&#34;duration&#34;</span>: <span style=color:#ae81ff>184.902</span>,
</span></span><span style=display:flex><span>    <span style=color:#f92672>&#34;fullname&#34;</span>: <span style=color:#e6db74>&#34;QuickTime / MOV&#34;</span>,
</span></span><span style=display:flex><span>    <span style=color:#f92672>&#34;bitrate&#34;</span>: <span style=color:#ae81ff>154062</span>,
</span></span><span style=display:flex><span>    <span style=color:#f92672>&#34;filesize&#34;</span>: <span style=color:#ae81ff>3.560797e6</span>,
</span></span><span style=display:flex><span>    <span style=color:#f92672>&#34;format&#34;</span>: <span style=color:#e6db74>&#34;mov,mp4,m4a,3gp,3g2,mj2&#34;</span>
</span></span><span style=display:flex><span>  }
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><h3 id=获取视频时长>获取视频时长</h3><p>OK，经过上面的接口，就拿到了视频的全部元信息，接下来就是不怎么重要的回扣标题一下。</p><p>在上面的示例数据中，可以找到 <code>format.duration</code> 字段，这就是我所要找的视频时长数据。✨🍰✨</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-python data-lang=python><span style=display:flex><span>meta <span style=color:#f92672>=</span> worker<span style=color:#f92672>.</span>get_meta(source)
</span></span><span style=display:flex><span>duration <span style=color:#f92672>=</span> meta <span style=color:#f92672>and</span> meta[<span style=color:#e6db74>&#39;format&#39;</span>][<span style=color:#e6db74>&#39;dutation&#39;</span>] <span style=color:#f92672>or</span> <span style=color:#66d9ef>None</span>
</span></span></code></pre></div><p>这里只记录获取视频元信息这一个 api 接口的使用。回看官方文档，其实有很多很多的服务可以使用，本地封装方法也类似上面这样，就可以自己写一个比官方更富有的 SDK 啦~</p><p>共勉！</p><p>&mdash; EOF &mdash;</p></div></article></main><aside><div><div><h3>LATEST POSTS</h3></div><div><ul><li><a href=/posts/2025-10-16-kubernetes-apiserver-authorization-mechanism/>Kubernetes APIServer 鉴权机制</a></li><li><a href=/posts/2025-09-30-kubernetes-apiserver-authentication-mechanism/>Kubernetes APIServer 认证机制</a></li><li><a href=/posts/2024-12-16-deploy-kubernetes-by-kubeadm/>使用 kubeadm 搭建 kubernetes 集群</a></li><li><a href=/posts/2024-09-20-how-to-code-review/>如何做code review</a></li><li><a href=/posts/2024-08-12-weakref-in-python/>Python中的弱引用</a></li></ul></div></div></aside><footer><p>Social Links:
<a href=https://github.com/vimiix><b>Github</b></a>.
<a href=https://www.douban.com/people/vimiix/><b>Douban</b></a>.
<a href=mailto:i@vimiix.com><b>Email</b></a>.<br><hr>&copy; 2017-2025
Vimiix Yao; All rights reserved.
<a href=https://beian.miit.gov.cn/>京ICP备19015214号-1</a></p><script src=https://l2dwidget.js.org/lib/L2Dwidget.min.js></script><script>L2Dwidget.init({model:{jsonPath:"https://unpkg.com/live2d-widget-model-tororo@1.0.5/assets/tororo.model.json"}})</script></footer></body></html>